using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using RFS.BizLogic.PaiShi.Ports.Adapters;
using RFS.BizLogic.Sys.impl;
using RFS.Domain.PaiShi;
using RFS.Domain.PaiShi.Accounting;
using RFS.Domain.PaiShi.WorkFlow;
using RFS.Domain.Sys;
using ycx;

namespace RFS.BizLogic.PaiShi.Accounting
{
    public class DayBasedWorkFlowNodeAccountingVoucherSetWrapper : IDayBasedWorkFlowNodeAccountingVoucherSetWrapper
    {
        private ConcurrentDictionary<ulong, IOrderItemBasedAccountingVoucher> _vouchers =
            new ConcurrentDictionary<ulong, IOrderItemBasedAccountingVoucher>();
        
 

        private VoucherSetState _state = VoucherSetState.准备;
        private IAccountingVoucherManager _vManager;


        private BizFlowNodeEnum _leftNode;
        private BizFlowNodeEnum _rightNode;
        private AccountPeriod _accPeriod = new AccountPeriod(DateTime.Today.AddDays(-2));

        public DayBasedWorkFlowNodeAccountingVoucherSetWrapper(  IAccountingVoucherManager vManager,
           BizFlowNodeEnum leftNode = BizFlowNodeEnum.FaHuo,
           BizFlowNodeEnum rightNode = BizFlowNodeEnum.YanShou
            )
        {
            // LeftNode = leftNode;
            // RightNode = rightNode;
            _vManager = vManager;
            _leftNode = leftNode;
            _rightNode = rightNode;

            CheckState();
        }

        public DayBasedWorkFlowNodeAccountingVoucherSetWrapper(BizFlowNodeEnum leftNode= BizFlowNodeEnum.FaHuo , BizFlowNodeEnum rightNode= BizFlowNodeEnum.YanShou)
        {
            _leftNode = leftNode;
            _rightNode = rightNode;
            _vManager = AntiObject.Ins.Get<IAccountingVoucherManager>();

            CheckState();
        }

        public override bool Equals(object obj)
        {
            // if (obj == null) return false;
            if (obj is IDayBasedWorkFlowNodeAccountingVoucherSetWrapper ot)return ot.Id.Equals(this.Id);
            return false;
            
        }

        public override int GetHashCode()
        {
            return Id.GetHashCode();
        }

        public override string ToString()
        {
            var p = _vouchers.Values.Count(x => x.IsBalanced);
            var c = _vouchers.Count;
            return $"已平{p}/{c}张，不平：{c - p}";
        }


        private bool _isLeftPeriodset = false;

        public AccountPeriod AccPeriod
        {
            get => _accPeriod;
            set
            {
                _accPeriod = value;
                // if (_isLeftPeriodset)
                    // LeftPeriod = new AccountPeriod(value.BeginDate.AddDays(-1));

            }
        }

        public void AddAv(IOrderItemBasedAccountingVoucher av)
        {
            // lock (_vouchers)
            // {
                //if (!av.IsBalanced)  throw new Exception($"[{av}]不平");
                _vouchers[av.Id] = av;
                // if (av.IsBalanced)
                // {
                    foreach (var o in av.AccItems)
                    {
                       if  (o.RelatedOrderItem is OrderItem ord ) ord.IsChecked= av.IsBalanced;
                    }
                // }

                CheckState();
                OnChanged();
            // }
          
        }


        public void CheckState()
        {
            Problem = ProblemClass.Of(); // no problem
            
            // if not orders left  
            if (LeftNode.WorkFlowNode.OrderItems.Any(x => !x.RecordedInAcc))
            {
                // State = VoucherState.待平;
            
                var msg = $"左边有[{LeftNode.WorkFlowNode.OrderItems.Count(x => !x.RecordedInAcc)}]个单待入账核销";
                Problem.AddSubProblem(msg, "left node", level: ProblemLevel.严重);
            }
            
            if (RightNode.WorkFlowNode.OrderItems.Any(x => !x.RecordedInAcc))
            {
                var msg = $"右边有[{LeftNode.WorkFlowNode.OrderItems.Count(x => !x.RecordedInAcc)}]个单待入账核销";
                // State = VoucherState.待平;
                Problem.AddSubProblem(msg, "r", level: ProblemLevel.警告);
            }
            
            else if (_vouchers.Any(x => !x.Value.IsBalanced))
            {
                // State = VoucherState.自平;
                var msg = $"有[{_vouchers.Values.Count(x => !x.IsBalanced)}]个凭证不平";
                // State = VoucherState.待平;
                Problem.AddSubProblem(msg, "r", level: ProblemLevel.警告);
            }
        }


        /// <summary>
        /// inlude all bad records
        /// </summary>
        public bool IsBalanced
        {
            get
            {
                // if there is seria problem
                if (Problem.Level >= ProblemLevel.严重) return false;
                return _vouchers.All(x => x.Value.IsBalanced);
            }
        }

        public Amount Balance
        {
            get => _vouchers.Values.Select(x => x.Balance).Sum();
        }
        // public event EventHandler SourceDataLoaded;


        #region SourceDataLoaded

        event EventHandler _SourceDataLoaded;

        public Amount DebitTotal { get=>_vouchers.Values.Select(x=>x.DebitTotal).Sum(); }
        public Amount CreditTotal { get=>_vouchers.Values.Select(x=>x.CreditTotal).Sum(); }
        public AccountPeriod LeftPeriod { get; set; }

        public event EventHandler SourceDataLoaded
        {
            add
            {
                _SourceDataLoaded += value;
                OnSourceDataLoaded();
            }
            remove => _SourceDataLoaded -= value;
        }

        void OnSourceDataLoaded()
        {
            _SourceDataLoaded?.Invoke(this, EventArgs.Empty);
        }

        #endregion

        // public event EventHandler Changed;


        #region Changed

        event EventHandler _Changed;

        public event EventHandler Changed
        {
            add
            {
                _Changed += value;
                OnChanged();
            }
            remove => _Changed -= value;
        }

          void  OnChanged()
        {
            1.DebounceExecute(() => { _Changed?.Invoke(this, EventArgs.Empty); }, 500, "myc");
        }       
        public void  OnChangedNow()
        {
              _Changed?.Invoke(this, EventArgs.Empty); 
        }

        #endregion


        public bool IsInRecordedAcc(IOrderItem o)
        {
            
            return _vouchers.Values.SelectMany(x => x.AccItems)
                    .Any(x => x.RelatedOrderItem.BizId == o.BizId && x.RelatedOrderItem.OrdType ==o.OrdType );
            
        }

        public IOrderItemBasedAccountingVoucher GetMyVoucherByLeftOrderId(string bizid)
        {
            // get one or create
            var v = _vouchers.Values.FirstOrDefault(x => x.AccItems.Count(b => b.RelatedOrderItem.BizId == bizid) > 0);

            return v;
        }

        public void CheckToCreateAVs(bool silent = false ,bool ignoreGhh=false )
        {
            var leftNode = LeftNode;
            var rightNode = RightNode;

            var osNeeded = leftNode.WorkFlowNode.OrderItems.Where(x => !x.RecordedInAcc)
                .ToList();

            var candidates = rightNode.WorkFlowNode.OrderItems.Where(x => !x.RecordedInAcc)
                .ToList();

            #region 1 dealing with good records

            var ts = osNeeded.Select(x => Task.Run(() =>
                {
                    var oa = x;
                    IOrderItem ob = null;
                    
                    if (!ignoreGhh)
                        ob= candidates.FirstOrDefault(z => z.Equals(oa));
                    else
                    {
                        ob= candidates.FirstOrDefault(z => z.Product.Equals(oa.Product) && z.Quantity==oa.Quantity );
                    }

                    
                    if (ob != null)
                    {
                        var av = CreateAV(oa, ob);
                        if (silent)
                        {
                            // lock (_vouchers)
                            // {
                                _vouchers[av.Id] = av;
                                foreach (var o in av.AccItems)
                                {
                                    if  (o.RelatedOrderItem is OrderItem ord ) 
                                        ord.IsChecked= av.IsBalanced;
                                }
                            // }
                        }
                        else
                            AddAv(av);
                    }
                })).ToArray()
                ;

            Task.WaitAll(ts);

            #endregion

            State = VoucherSetState.已对账;
            OnChanged();
            OnSourceDataLoaded();

            
        }

        public IOrderItemBasedAccountingVoucher CreateAV(IOrderItem d, IOrderItem c)
        {
            var a = CreateAV(new List<IOrderItem>(){d} ,new List<IOrderItem>(){c}   );
             
            return a;
        }
        
        
        Amount GetAmount(IOrderItem orderItem)
        {
           
            return new Amount( amount: orderItem.Amount ,quantity: orderItem.Quantity );
        }
        public IOrderItemBasedAccountingVoucher CreateAV( IEnumerable<IOrderItem> ghos,  IEnumerable<IOrderItem> ysos)
        {
            //1 create a av object and put it into 

            var av = new OrderItemBasedAccountingVoucher();
            
            foreach (var gho in ghos)
            {
                if (gho != null)
                {
                    av.AddAccItem(gho, Account.KCSP, $"供货_{gho.Product.Key}_{gho.BizId}", amountDebit: GetAmount(gho)); //j
                    (gho as OrderItem).RecordedInAcc = true;
                }
            
            }
            foreach (var yso in ysos)
            {
                if (yso != null)
                {
                    av.AddAccItem(yso, Account.KCSP, $"收货_{yso.Product.Key}_{yso.BizId}", amountCredit: GetAmount(yso)); //d
                    (yso as OrderItem).RecordedInAcc = true;
                     

                }
              
            }
            
            // av.State = VoucherState.自平;
            AddAv(av);
            return av;
        }

        public void GotoDefferenceProcess()
        {
            "请先对账".Required(State== VoucherSetState.已对账 ||  State== VoucherSetState.差异处理);

            ReProcessDefferencesInner( true);
            State = VoucherSetState.差异处理;

            OnChanged();
        }
        public void ReProcessDefferences (bool overrideData=false)
        {
            ReProcessDefferencesInner(overrideData);
            OnChanged();
        }         
        private void ReProcessDefferencesInner (bool overrideData=false)
        {
            
            if (!overrideData &&   _vouchers.Values.Any(x=>x.VType== VType.差异处理))
                return;


            var needtoclean=ManualVouchers.Where(x => x.VType == VType.差异处理).ToList();
            foreach (var d in needtoclean)
            {
                RemoveAV(d);
            }
            
            
            // 1 get all 
            var ls = LeftNode.DateItems.Where(x => !IsInRecordedAcc(x)).ToList();
            var rs = RightNode.DateItems.Where(x => !IsInRecordedAcc(x)).ToList();

            var av=CreateAV(ls, rs);
            av.VType = VType.差异处理;
            _vouchers[av.Id] = av;

            State = VoucherSetState.差异处理;

        }
        
        public void RemoveAV (IOrderItemBasedAccountingVoucher o)
        {

            if (_vouchers.ContainsKey(o.Id))
            {
                var ov = _vouchers[o.Id];
                foreach (var od in ov.AccItems)
                {
                    if (od.RelatedOrderItem is OrderItem orderItem)
                    {
                        orderItem.RecordedInAcc=false;
                        orderItem.IsChecked = false;
                    }
                }
                _vouchers.TryRemove(o.Id, out _ );
                
                OnChanged();

            }
            
        }

        public IOrderItemBasedAccountingVoucher GetMyVoucherByOrder(IOrderItem orderItem)
        {
            var o = orderItem;

            var v = _vouchers.Values.FirstOrDefault(x => x.AccItems
                .Any(y => y.RelatedOrderItem.BizId == o.BizId && y.RelatedOrderItem.OrdType == o.OrdType));
                 
            return v;
        }

      


        public IEnumerable<IOrderItemBasedAccountingVoucher> ManualVouchers {get=>_vouchers.Values.Where(x=>x.VType!= VType.系统生成).ToList();} 


        public async Task LoadLeftNodeDataAsync(BizFlowNodeEnum leftNode= BizFlowNodeEnum.FaHuo)
        {
            GotoPreparingData();
            "已处于【对账】状态".Required(State== VoucherSetState.准备);

            await Task.Run(() =>
            {

                try
                {
                    LeftNode = _vManager.GetNode(LeftPeriod,  AccPeriod , leftNode);
                    _leftNode = leftNode;

                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    
                }

                OnChanged();
                OnSourceDataLoaded();
            });
        }

        public async Task LoadRightNodeDataAsync(bool fromProdLine, bool regroup)
        {
            "已处于【对账】状态".Required(State== VoucherSetState.准备);

            await Task.Run(() =>
            {
                RightNode.ClearData();

                try
                {
                    RightNode = _vManager.GetRightNode(AccPeriod, fromProdLine, regroup);

                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
                
                OnChanged();
                OnSourceDataLoaded();
            });
        }

        public IEnumerable<IOrderItem> GetYSCandiatesFor(
            OrderItemBasedAccountingVoucher orderItemBasedAccountingVoucher, bool includeHistory = false,
            bool compareCash = true)
        {
            var selectionBase = RightNode.WorkFlowNode.OrderItems.ToList();

            var IncludeHistoryCandiateAlso = includeHistory;


            if (IncludeHistoryCandiateAlso)
            {
                var allHistory = _vManager.GetHistoryUnRecorderOrder(AccPeriod).WorkFlowNode.OrderItems;


                var usedIds = _vouchers.Values.SelectMany(x => x.AccItems).Select(x => x.RelatedOrderItem.BizId);


                var notUsedOnes = allHistory.Where(x => !usedIds.Contains(x.BizId));

                //get all history un checked   orders
                selectionBase.AddRange(notUsedOnes);
            }

            //also 
            if (compareCash)
            {
                // get all left node 
                var res = selectionBase
                    .Where(x => !x.RecordedInAcc);


                return res;
            }
            else
            {
                //compare the quantity 

                var myProds = orderItemBasedAccountingVoucher.AccItems.Select(x => x.RelatedOrderItem.Product)
                    .ToList();


                // compare the product only
                var res = selectionBase
                    .Where(x => !x.RecordedInAcc && myProds.Contains(x.Product));

                return res;
            }
        }


        public IEnumerable<IOrderItemBasedAccountingVoucher> Vouchers
        {
            get { return _vouchers.Values; }
            set
            {
                _vouchers.Clear();
                _vouchers =
                    new ConcurrentDictionary<ulong, IOrderItemBasedAccountingVoucher>(
                        value.ToDictionary(x => x.Id, y => y));
                
            }
        }

        public IWorkFlowNodeWrapper LeftNode { get; set; } = new WorkFlowNodeWrapper();
        public IWorkFlowNodeWrapper RightNode { get; set; } = new WorkFlowNodeWrapper();


        public event EventHandler<(VoucherSetState oldState, VoucherSetState newStete)> StateChanged;
        public void GotoPreparingData()
        {
            _vouchers.Clear();
            LeftNode.ClearData();
            RightNode.ClearData();
            State = VoucherSetState.准备;

            OnSourceDataLoaded(); // node the source
        }

        void OnStateChanged(VoucherSetState oldState, VoucherSetState newState)
        {
            StateChanged?.Invoke(this, (oldState, newState));
        }

        public VoucherSetState State
        {
            get => _state;
            set
            {
                var oldState = _state;
                _state = value;

                OnStateChanged(oldState, value);
            }
        }

        public string KeyNote
        {
            get => $"左：{LeftNode.KeyNote}<-->右：{RightNode.KeyNote}";
        }

        public IProblem Problem { get; private set; } = new ProblemClass();
        public ulong Id { get; set; }
    }


    public enum VoucherSetState
    {
        准备,
        差异处理,
        已对账,
    }
}